<template>
  <div class="reader">
    <div v-html="info.tip"></div>
    <hr/>
    <button @click="getCameras">扫码</button>  ====  <button @click="stop">取消扫码</button>
    <div style="height: 100%; width: 100%">
      <div class="qrcode">
        <div id="reader"></div>
      </div>
    </div>
<!-- 
    <div class="stream">
      <div class="qrcode">
        <div id="render"></div>
      </div>
    </div>

    <button class="sweep" @click="getCamera">扫一扫</button>
    <button class="sweep" @click="stopCamera">取消扫码</button>
    <div class="toolbar">
      <p v-show="error">{{error}}</p>
      <button @click="torch = !torch">{{torch? '关闭闪光灯' : '开启闪光灯'}}</button>
      <button @click="switchCamera">{{'rear' == camera? '前置摄像头' : '后置摄像头'}}</button>
    </div>
-->
    <textarea class="result" v-model="info.result" placeholder="二维码识别结果！"></textarea> 

    <hr />
    <p>
      注：<br/>
      支持扫描不同类型的条形码和二维码<br/>
      支持不同的浏览器、不同平台<br/>
      支持闪光<br/>
      直接访问摄像头，涉及到隐私，所以环境必须是 `HTTPS`
    </p>
  </div>
</template>


<script>
import { defineComponent, reactive, toRefs } from "vue";
import { Html5Qrcode } from 'html5-qrcode';
import tone from '@/assets/tone.mp3';

export default defineComponent({
  name: 'h5Qrcode',
  components: {
   
  },
  setup() {
    let info = reactive({
      tip: '',
      result: ''
    });
    let data = reactive({
      /**
       * https://hj29.github.io/vue3-qr-reader/
       */
      error: '',          // 扫一扫错误信息
      result: '',         // 二维码识别结果
      stream: false,      // 显示扫一扫相机
      torch: false,       // 是否开启闪光灯
      camera: 'rear',     // rear（后置摄像头[默认]） | front（前置摄像头）
      capture: false,     // environment（后置摄像头[默认]） | user（前置摄像头） | false（相册[图片库]）
    });

    // 扫码初始化
    let cameraId = ''
    let html5QrCode = null
    const start = () => {
      html5QrCode.start(
      // environment后置摄像头 user前置摄像头
      { facingMode: "environment" },
      {
        fps: 2, // 可选，每秒帧扫描二维码
        qrbox: { width: 250, height: 250 } // 可选，如果你想要有界框UI
        // 设置扫描结果的宽高比为1.777778，即宽高比为根号2，即等腰梯形的宽高比 
        // aspectRatio: 1.777778 // 可选，视频馈送需要的纵横比，(4:3--1.333334, 16:9--1.777778, 1:1--1.0)传递错误的纵横比会导致视频不显示
      },
      (decodedText, decodedResult) => {
        // 当成功读取到QR码时，执行此回调函数
        console.log('decodedText', decodedText)
        console.log('decodedResult', decodedResult)
        info.result = decodedText
      }).catch((err) => {
        console.log('扫码错误信息', err)
        // 错误信息处理仅供参考，具体情况看输出！！！
        if (typeof err == 'string') {
          // this.$toast(err)
        } else {
          // if (err.name == 'NotAllowedError') return this.$toast("您需要授予相机访问权限")
          // if (err.name == 'NotFoundError') return this.$toast('这个设备上没有摄像头')
          // if (err.name == 'NotSupportedError') return this.$toast('摄像头访问只支持在安全的上下文中，如https或localhost')
          // if (err.name == 'NotReadableError') return this.$toast('相机被占用')
          // if (err.name == 'OverconstrainedError') return this.$toast('安装摄像头不合适')
          // if (err.name == 'StreamApiNotSupportedError') return this.$toast('此浏览器不支持流API')
        }
      })
    }
    const getCameras = () => {
      info.tip = '开始'
      Html5Qrcode.getCameras()
      .then((devices) => {
        /**
         * devices 是对象数组
         * 例如：[ { id: "id", label: "label" }]
         */
        if (devices && devices.length) {
          if (devices.length > 1) {
            cameraId = devices[1].id
          } else {
            cameraId = devices[0].id
          }
          info.tip += '---已获取到摄像头:' + cameraId
          html5QrCode = new Html5Qrcode("reader")
          start()
        }
      }).catch((err) => {
        html5QrCode = new Html5Qrcode("reader")
        console.log('启用相机失败,需要授予相机访问权限')
      })
    }
    const stop = () => {
      html5QrCode.stop().then((ignore) => {
        html5QrCode.clear()
        console.log("二维码扫描已停止.")
      }).catch((err) => {
        console.log("无法停止扫描.")
      })
    }

    // const audio = new Audio(tone);

    // // 扫一扫初始化【注：不要连续多次扫描同一个二维码！！！】
    // const onInit = async (promise) => {
    //   try {
    //     await promise
    //   } catch (error) {
    //     if (error.name === 'NotAllowedError') {
    //       data.error = "错误：您需要授予相机访问权限！"
    //     } else if (error.name === 'NotFoundError') {
    //       data.error = "错误：此设备上没有摄像头！"
    //     } else if (error.name === 'NotSupportedError') {
    //       data.error = "错误：需要安全上下文（HTTPS，本地主机）！"
    //     } else if (error.name === 'NotReadableError') {
    //       data.error = "错误：相机是否已经在使用？"
    //     } else if (error.name === 'OverconstrainedError') {
    //       data.error = "错误：安装的摄像头不合适！"
    //     } else if (error.name === 'StreamApiNotSupportedError') {
    //       data.error = "错误：此浏览器不支持 Stream API！"
    //     } else if (error.name === 'InsecureContextError') {
    //       data.error = '错误：仅在安全上下文中允许访问相机。使用 HTTPS 或 localhost 而不是 HTTP！';
    //     } else {
    //       data.error = `错误：相机错误（${error.name}）！`;
    //     }
    //   }
    // };

    // // 识别结果回调
    // const onDecode = (res) => {
    //   audio.play();
    //   setTimeout(() => {
    //     data.result = res;
    //     data.stream = false;
    //   }, 600);
    // };

    // // 切换前后置摄像头
    // const switchCamera = () => {
    //   switch (data.camera) {
    //     case 'front':
    //       data.camera = 'rear';
    //       break
    //     case 'rear':
    //       data.camera = 'front';
    //       break
    //   }
    // };

    // // 改变扫一扫选框
    // const stroke = ['red', 'green', 'blue'];
    // const paintBoundingBox = (detectedCodes, ctx) => {
    //   const strokeStyle = stroke[parseInt(Math.random() * 3)];
    //   for (const detectedCode of detectedCodes) {
    //     const { boundingBox: { x, y, width, height } } = detectedCode;
    //     ctx.lineWidth = 2;  // 选框宽度
    //     ctx.strokeStyle = strokeStyle; // 选框颜色
    //     ctx.strokeRect(x, y, width, height);
    //   }
    // };

    // // 错误回调
    // const onError = (res) => {
    //   data.result = res;
    //   data.stream = false;
    // };

    return {
      getCameras,
      stop,
      // onInit,
      // onError,
      // onDecode,
      // switchCamera,
      // paintBoundingBox,
      info,
      data
    };
  }
});
</script>


<style lang="less">
.reader {
  font-size: 16px;

  .sweep {
    position: relative;
    margin: 20px;
    padding: 12px;
    width: 300px;
    font-size: 18px;
    color: white;
    background: #42b983;
    border: 1px solid #42b983;
    overflow: hidden;
    cursor: pointer;

    input {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      cursor: pointer;
      opacity: 0;
    }
  }

  .stream {
    position: fixed !important;;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
    background: rgba(0, 0, 0, 0.5);

    p {
      color: red;
      text-align: center;
    }

    button {
      margin: 20px auto;
      border: 1px solid gray;
      background: rgba(255, 255, 255, 0.5);
    }
  }

  .result {
    box-sizing: border-box;
    display: block;
    margin: 10px auto;
    padding: 10px;
    width: 80%;
    min-height: 180px;
    font-size: 22px;
    border-radius: 6px;
    border: 1px solid gray;
  }

}
</style>